#pragma once

#ifndef __UDP_SERVER_HPP__
#define __UDP_SERVER_HPP__

#include "Common.hpp"
#include "Log.hpp"
#include "InetAddr.hpp"

using namespace LogModule;
using func_t = function<string(const string&)>;

const static int gsockfd = -1;
// const static string gdefaultip = "127.0.0.1";  // 表示本地主机
const static uint16_t gdefaultport = 8080;  

class UdpServer
{
public:
    UdpServer(/* const string& ip = gdefaultip, */ func_t func, uint16_t port = gdefaultport)
        :_sockfd(gsockfd)
        // ,_ip(ip)
        ,_addr(port)  // 使用InetAddr的构造函数
        ,_isrunning(false)
        ,_func(func)
    {}

    void InitServer()
    {
        // 1. 创建socket
        _sockfd = ::socket(AF_INET, SOCK_DGRAM, 0);
        if(_sockfd < 0)  // 创建失败返回 -1
        {
            LOG(LogLevel::FATAL) << "socket: " << strerror(errno);
            Die(SOCKET_ERR);
        } 
        LOG(LogLevel::INFO) << "socket successed! sockfd is : " << _sockfd;
        // 2. 填充网络信息， 并bind绑定
        // 此时并没有socket信息设置进内核里，只是将local的信息在栈上填充了
        // struct sockaddr_in local;
        // bzero(&local, sizeof(local));  // bzero 函数可以将指定数据清零
        // local.sin_family = AF_INET;
        // local.sin_port = ::htons(_port);  // 端口号是要被发送给对方的，即发送到网络中的
        // // local.sin_addr.s_addr = ::inet_addr(_ip.c_str());
        // local.sin_addr.s_addr = INADDR_ANY;

        // 只有绑定了，才是真正设置进内核中了
        int n = ::bind(_sockfd, _addr.NetAddr(), _addr.NetAddrLen());
        if(n < 0)  // 返回 -1，绑定失败
        {
            LOG(LogLevel::FATAL) << "bind: " << strerror(errno);
            Die(BIND_ERR);
        }
        LOG(LogLevel::INFO) << "bind successed";
    }

    void Start()
    {
        _isrunning = true;
        while(true)
        {
            char inbuffer[1024];
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);  // 必须设定
            ssize_t n = ::recvfrom(_sockfd, inbuffer, sizeof(inbuffer)-1, 0, CONV(&peer), &len);  // 谁给我发的信息？存入到了 peer 里
            if(n > 0)  // 说明接受成功，返回读取的字节数
            {
                // 我需要获取消息是谁发的 && 消息内容
                // uint16_t clientport = ::ntohs(peer.sin_port);   // 网络的转为普通的
                // string clientip = ::inet_ntoa(peer.sin_addr);  // 网络的转为字符串的 
                // InetAddr cli(peer);
                inbuffer[n] = 0;
                // string clientinfo = cli.Ip() + ":" + to_string(cli.Port()) + " #" + inbuffer;
                // LOG(LogLevel::DEBUG) << clientinfo; 
    
                // string echo_string = "echo# ";
                // echo_string += inbuffer;

                // 将英文单词转成中文
                string result = _func(inbuffer);  // 这个是回调，调用完成之后还会回来
                ::sendto(_sockfd, result.c_str(), result.size(), 0, CONV(&peer), sizeof(peer));
            }
        }
        _isrunning = false;
    }

    ~UdpServer()
    {
        if(_sockfd > gsockfd)
            ::close(_sockfd);
    }
private:
    int _sockfd;
    InetAddr _addr;
    // uint16_t _port;  // 服务器未来的端口号
    // string _ip;  // 服务器未来的ip地址
    bool _isrunning;  // 服务器的运行状态
    func_t _func;  // 业务，让上层告诉我想让我这个服务器干什么，比如传进来什么英文单词，输出对应的中文
};

#endif